#ifndef STREETMAP_H
#define STREETMAP_H

#include <QString>
#include <QSize>
#include <QList>
#include <QMap>
#include "point.h"
#include "linesegment.h"

#define LANE_WIDTH 12 // feet

class StreetMap
{
  public:
    class Road;
    class Intersection;
    typedef QList<Road*> RoadList;
    typedef QList<Intersection*> IntersectionList;
    typedef QMap<QString, Intersection*> IntersectionMap;

    StreetMap();
    StreetMap(const QString& fileName, const QSize& size);
    ~StreetMap();

    IntersectionList getIntersections() const;
    const Intersection* getIntersection(const GeometryUtilities::Point& point) const;
    Intersection* getIntersection(const GeometryUtilities::Point& point);
    const Intersection* getIntersection(const Intersection* previousIntersection, const Road* currentRoad) const;
    const Intersection* getIntersection(const QString& name) const;
    void createIntersectionGaps();

    RoadList getRoads() const;
    RoadList getRoads(const Intersection* intersection) const;
    const Road* getRoad(const Intersection* intersection1, const Intersection* intersection2) const;
    const Road* getRoad(const GeometryUtilities::Point& point) const;
    const Road* getRoad(const QString &roadName) const;

  protected:
    RoadList mRoads;
    IntersectionMap mIntersections;
};

class StreetMap::Road
{
  public:
    class Lane
    {
    public:
      enum DIRECTION {NO_LANE, FROM_FIRST_TO_SECOND, FROM_SECOND_TO_FIRST, BOTH};
      // These can be bitwise or'd together and then masked away to make sure actions are available
      enum TURN_TYPE {NO_TYPE=0x0, STRAIGHT=0x1, RIGHT=0x2, LEFT=0x4};

      DIRECTION direction;
      int type;
      GeometryUtilities::LineSegment segment;
    };
    typedef QList<Lane> LaneList;

    Road();
    Road(unsigned int speedLimit, const QString& streetName, Intersection* first, Intersection* second);

    const QString& streetName() const;
    void setStreetName(const QString& newName);
    qreal length() const;
    unsigned int width() const;
    unsigned int speedLimit() const;
    //const Intersection *first() const;
    //const Intersection *second() const;
    const GeometryUtilities::Point& first() const;
    const GeometryUtilities::Point& second() const;
    GeometryUtilities::Point& first();
    GeometryUtilities::Point& second();
    const Intersection *getFirstIntersection() const;
    const Intersection *getSecondIntersection() const;
    Intersection *getFirstIntersection();
    Intersection *getSecondIntersection();
    Lane::DIRECTION getDirection(const Intersection *goingTowards) const;

    // If after the call valid is false, the item returned is invalid.
    const Lane& lane(Lane::DIRECTION d, int lanePos, bool& valid) const;
    unsigned int numLanes(Lane::DIRECTION d) const;
    unsigned int numLanes() const;
    // If replace is true and a lane at lanePos already exists, this method will
    // replace it's type with the desired type.
    // If lanePos is greater than the current number of lanes (as should generally be the case),
    // this method will add straight lanes until the desired lane, and then set it.
    void addLane(int lanePos, Lane::TURN_TYPE type, Lane::DIRECTION dir, bool replace=true);
    void addLane(const Lane& l);

    void recalculateLaneSegments();

protected:
    QString mStreet;
    QList<Lane>  mFirstToSecondLanes, mSecondToFirstLanes;
    unsigned int mSpeedLimit;
    GeometryUtilities::Point mFirst, mSecond;
    Intersection *mIFirst, *mISecond;
};

class StreetMap::Intersection
{
  public:
    Intersection();
    Intersection(const QString& name, const GeometryUtilities::Point& location);
    const QString& name() const;

    const GeometryUtilities::Point& location() const;
    void addNeighbor(Intersection* neighbor);

  protected:
    QString mName;
    QList<Intersection*> mNeighbors;
    GeometryUtilities::Point mLocation;
};

#endif // STREETMAP_H
